--[[
	Garen Ultimate Helper v0.7
	Automatically cast Garen's ultimate when someone in range can die from it
	by NewHotness
]]

--[[		Config		]]

--Mode Settings
local cancelSpinToUlt = true --will cancel spin to ult instead of finishing
local qPlusUlt = true
local autoSpinOnKillable = true
local active = true
local towerTable, heros
local player = GetMyHero()
--[[		Code		]]


local function hasBladestorm()
    if lastUsed then
        if os.clock() - lastUsed > 3 then lastUsed = nil
        else
            return true, math.ceil(6 - ((os.clock() - lastUsed) * 2))
        end
    end
    return false, player:CanUseSpell(_E) == READY and 6 or 0
end

local function hasShield(target)
    if active then player:Attack(target) end
    local count = target.buffCount
    for i = 1, count, 1 do
        if target:getBuff(i) then
            local buff = string.lower(target:getBuff(i))
            if buff:find("spellshield") or buff:find("bansheesveil") or buff:find("undyingrage") or buff:find("judicatorintervention") or buff:find("nocturneshroudofdarkness") then
                PrintChat(target.charName .. " has shield, let's not cast right now..")
                return true
            end
        end
    end
    return false
end

function OnTick()
    if Activated and (not lastUpdated or os.clock() - lastUpdated > 0.25) then
        lastUpdated = os.clock()
        local function shortWayOut()
            local time, x, z = 0, nil, nil
            local distnearestTower = math.huge
            for i, tower in ipairs(towerTable) do
                if tower.valid and tower.health > 0 and player:GetDistance(tower) <= 800 and distnearestTower > player:GetDistance(tower) then
                    distnearestTower = player:GetDistance(tower)
                    local r = 0
                    repeat
                        r = r + 0.1
                        until 850 <= math.sqrt((r * (player.x - tower.x)) ^ 2 + (r * (player.z - tower.z)) ^ 2)
                    x = tower.x + r * (player.x - tower.x)
                    z = tower.z + r * (player.z - tower.z)
                    time = math.sqrt((x - player.x) ^ 2 + (z - player.z) ^ 2) / player.ms
                end
            end
            return time, x, z
        end

        if lastSpinTarget and (lastSpinTarget.dead or player:GetDistance(lastSpinTarget) > 330) then -- you lost your target
            local time, x, z = shortWayOut()
            lastSpinTarget = nil
            if x and z and active == true then player:MoveTo(x, z) end --if you are under a turret, go out!
        end
        local player = player
        for i, target in ipairs(heros) do
            local function getTowerDamage(time)
                local damage = 0
                for i, tower in ipairs(towerTable) do
                    if tower.valid and tower.health > 0 and player:GetDistance(tower) <= 800 + 5 then --tower range is 750
                        local count = 0
                        for i = 1, time, 1 do
                            local shotdamage = tower:CalcDamage(player, tower.totalDamage * (i * 0.225 + 1))
                            if shotdamage > tower:CalcDamage(player, tower.damage * (2.35)) then shotdamage = tower:CalcDamage(player, tower.damage * (2.35)) end
                            damage = damage + shotdamage
                            count = count + 1
                        end
                    end
                end
                return damage
            end

            if target.visible and not target.dead and player:GetDistance(target) < 1200 then
                local qDmg = (player:GetSpellData(_Q).level < 1 or not player:CanUseSpell(_Q) == READY) and 0 or player:CalcDamage(target, (30 + (15 * (player:GetSpellData(_Q).level - 1))) + (player.totalDamage * 1.4))
                local rDmg = (player:GetSpellData(_R).level < 1 or not player:CanUseSpell(_R) == READY) and 0 or player:CalcMagicDamage(target, (target.maxHealth - target.health) / (3.5 - ((player:GetSpellData(_R).level - 1) * .5)) + (player:GetSpellData(_R).level * 175))
                local bladestorm, remainingHits = hasBladestorm()
                local eDmg = player:GetSpellData(_E).level < 1 and 0 or remainingHits * player:CalcDamage(target, player.totalDamage * 0.7 + 25 + (player:GetSpellData(_E).level - 1) * 20)

                if qDmg > 0 and qDmg + eDmg + rDmg > target.health and player:GetDistance(target) < 500 and (shortWayOut() == 0 or (player.level >= 16 and player.health / player.maxHealth > 0.9)) then
                    CastSpell(_Q)
                    if active then player:Attack(target) end
                end

                if autoSpinOnKillable and eDmg > 0 and eDmg + rDmg > target.health and player:GetDistance(target) < 330 and (getTowerDamage(math.ceil((remainingHits * target.health) / eDmg) + shortWayOut()) == 0 or (player.health - getTowerDamage(math.ceil((remainingHits * target.health) / eDmg) + shortWayOut())) / player.maxHealth > 0.4) then
                    if player:CanUseSpell(_E) == READY then
                        if active and target.x and target.z then player:MoveTo(target.x, target.z) end
                        CastSpell(_E)
                        lastSpinTarget = target
                        return
                    elseif hasBladestorm() and active and target.x and target.z then
                        player:MoveTo(target.x, target.z)
                        return
                    end
                else
                    lastSpinTarget = nil
                end
                if target.health < rDmg and player:GetDistance(target) < 400 and player:CanUseSpell(_R) == READY and not hasShield(target) then
                    if cancelSpinToUlt and hasBladestorm() then
                        CastSpell(_E)
                        return
                    end
                    CastSpell(_R, target)
                end
            end
        end
    end
end

function OnProcessSpell(object, spell)
    if Activated then
        if player:CanUseSpell(_W) == READY and object.type == "obj_AI_Hero" and object.team == TEAM_ENEMY then
            if math.sqrt((player.x - spell.endPos.x) ^ 2 + (player.z - spell.endPos.z) ^ 2) < 100 then
                CastSpell(_W)
            end
        elseif object == player and spellName == "GarenBladestorm" then
            lastUsed = os.clock()
        end
    end
end

function OnWndMsg(msg, key)
    if Activated and key == 32 then
        if msg == KEY_DOWN then active = false
        else active = true
        end
    end
end

function OnLoad()
    if player.charName ~= "Garen" then
        PrintChat(" >> You are not playing as Garen. Unloading...")
    else
        towerTable = {}
        for i = 1, objManager.maxObjects, 1 do
            local object = objManager:getObject(i)
            if object and object.type == "obj_AI_Turret" and object.health > 0 and object.team == TEAM_ENEMY then
                table.insert(towerTable, object)
            end
        end
        heros = {}
        for i = 1, heroManager.iCount, 1 do
            local hero = heroManager:getHero(i)
            if hero.team ~= player.team then
                table.insert(heros, hero)
            end
        end
        Activated = true
        PrintChat(" >> Garen Ultimate Helper loaded!")
    end
end